﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace VCDIFF.Tools
{
    /// <summary>
    /// This class contains static methods to encode and decode integer using the SFIO encoding.
    /// </summary>
    public class SFIOInteger
    {
        /// <summary>
        /// Encodes an UInt64 value to a data table.
        /// </summary>
        /// <param name="value">The value to encode.</param>
        /// <returns>The encoded data table.</returns>
        public static byte[] Encode(UInt64 value)
        {
            UInt64 copy = value;
            int size = 1;

            while (copy > 127)
            {
                copy /= 128;
                size++;
            }

            byte[] encoded = new byte[size];

            for (int i = 0; i < encoded.Length; i++)
            {
                if (i == 0)
                    encoded[i] = Convert.ToByte(0x00 + value % 128);
                else
                    encoded[i] = Convert.ToByte(0x80 + value % 128);
                value /= 128;
            }

            byte[] encodedReversed = new byte[size];
            int index = 0;

            for (int i = size - 1; i >= 0; i--)
            {
                encodedReversed[index] = encoded[i];
                index++;
            }

            return encodedReversed;
        }

        /// <summary>
        /// Decodes a SFIO encoded integer from an open BinaryReader.
        /// </summary>
        /// <param name="reader">A BinaryReader instance on an open stream.</param>
        /// <returns>The decoded integer.</returns>
        public static UInt64 Decode(BinaryReader reader)
        {
            List<byte> encoded = new List<byte>();

            // Reads the stream as there should be some valid integer
            byte b;
            do
            {
                b = reader.ReadByte();
                encoded.Add(b);
            } while (b > 0x7F);

            return Decode(encoded.ToArray());
        }

        /// <summary>
        /// Decodes a SFIO encoded integer from a data table.
        /// </summary>
        /// <param name="data">The data table to decode</param>
        /// <returns>The decoded integer.</returns>
        public static UInt64 Decode(byte[] data)
        {
            UInt64 decoded = 0, factor = 1;

            for (int i = data.Length - 1; i >= 0; i--)
            {
                decoded += (UInt64)(data[i] & 0x7F) * factor;
                factor *= 128;
            }

            return decoded;
        }

        /// <summary>
        /// Gets the size of a SFIOInteger from a data table at the specified beginning.
        /// </summary>
        /// <param name="data">Data table where the SFIOInteger is contained.</param>
        /// <param name="beginning">The position of the first byte to test.</param>
        /// <returns>The size of the SFIOInteger</returns>
        public static UInt64 Size(byte[] data)
        {
            UInt64 integerSize = 0;

            while (data[integerSize] > 0x7F)
                integerSize++;

            return integerSize;
        }
    }
}
